package sd.settemmezzo.player;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

import javax.swing.JOptionPane;

import org.apache.log4j.Logger;

import sd.settemmezzo.common.Player;
import sd.settemmezzo.common.PlayerInterface;
import sd.settemmezzo.graphic.UI_Client;
import sd.settemmezzo.player.state.GlobalState;
import sd.settemmezzo.player.state.LocalState;
import sd.settemmezzo.player.thread.SendThreadFactory;
import sd.settemmezzo.player.thread.WaitForInput;
import sd.settemmezzo.util.Debug;
import sd.settemmezzo.util.Ring;
import sd.settemmezzo.util.Util;
import sd.settemmezzo.util.Message;

public class PlayerImplement extends UnicastRemoteObject implements PlayerInterface {

	private static final long serialVersionUID = 1345979098512824760L;
	private static Logger log = Logger.getLogger("PlayerImplement");

	final boolean DEBUG = false;
	
	public static LocalState  lstate;
	public static GlobalState gstate;
	private UI_Client ui;
	
	private boolean pingStarted = false;
	private boolean gameStarted = false;

	private SendThreadFactory threads;
	
	protected PlayerImplement(String name) throws RemoteException {
		super();
		lstate = LocalState.getInstance();
		gstate = GlobalState.getInstance();
		this.threads = new SendThreadFactory();
		this.ui = UI_Client.getInstance(name);
	}
	
	/* Quando ricevo la lista dei giocatori dal server */
	public synchronized void setPlayersList(Ring playersList) throws RemoteException {		
		
		log.info("[setPlayersList] " + Debug.printPlayersList(playersList));
		
		/* Calcolo il mio nextPlayer e lo salvo */
		lstate.setNextPlayer(playersList.getNext(lstate.getId()));
		//log.info("[setPlayersList] NextPlayer in lstate: " + playersList.getNext(lstate.getId()).getId() + ":" + playersList.getNext(lstate.getId()).getNamePlayer());
		
		/* Calcolo se sono il leader secondo l'algoritmo del bullo */
		if (lstate.getId() == playersList.lastElement().getId() && !gameStarted) {
			
			lstate.setRuolo("leader");
			
			/* Creo lo stato condiviso */
			gstate.setLeader(Util.electBully(playersList));
			
			/* Il mazziere fa anche da leader */
			gstate.setMazziere(gstate.getLeader());
			// gstate.setMazziere(playersList.get((int) (Math.random() * playersList.size())));
			
			/* Inizia il giocatore alla destra del mazziere */
			gstate.setToken(playersList.getNext(lstate.getId()).getId());
			
			gstate.setRing(playersList);
			
			/* Distribuisco le carte coperte iniziali */
			gstate = Util.carteCoperte(gstate);
	
			/* Inoltro lo stato condiviso al mio successivo */
			log.info("[setPlayersList] Sono il mazziere, inoltro al nodo successivo lo stato globale. "
					+ "Il turno è del nodo " + gstate.getToken());
			
			Message msg = new Message(Message.GS_UPDATE, gstate);
			threads.startSendThread(msg, lstate.getNextPlayer());
		}
				
		if (gameStarted) {
			gstate.setRing(playersList);
			if (playersList.lastElement().getId() != gstate.getMazziere().getId()) {
				gameStarted = false;
				setPlayersList(playersList);
			} else {
				Message msg = new Message(Message.FINE_MANO, gstate);
				threads.startSendThread(msg, lstate.getNextPlayer());
			}
		} else {
			gameStarted = true;
		}
		
	}
	
	/* Ho ricevuto un aggiornamento dello stato globale */
	//public synchronized void sendMessage(GlobalState gs) throws RemoteException {
	public synchronized void send(Message m) throws RemoteException {
		
		GlobalState gs = null;
		
		switch (m.getType()) {
		
		case Message.CRASH_DETECTED:
			
			gs = (GlobalState) m.getObject();
			
			if (!gs.getRing().contains(gstate.getMazziere())) {
				Util.carteCoperte(gs);
				gs.setToken(gs.getRing().getNext(gs.getMazziere()).getId());
				gs.setMessage("Tavolo senza mazziere!\nRicomincio una nuova mano...");
			}
			
			if (m.getSender() == lstate.getId()) {
				gs.notifyChanged(ui);
				gs.setMessage(null);
			}
		
			// continue...
			
		case Message.GS_UPDATE:

			if (gs == null)
				gs = (GlobalState) m.getObject();
			
			log.info("[setGlobalState] Ho ricevuto un aggiornamento dello stato globale");
			
			/* Se siamo rimasti in uno, ho vinto */
			if (gs.isWinner(lstate.getId())) {
				// metodoVittoria();
				log.info("!!! HAI VINTO !!!");
				JOptionPane.showMessageDialog(ui, "HAI VINTO!!!!");
				return;
			}
			
			/* Se è il mio turno... */
			if (gstate.equals(gs) && (gstate.getToken() == lstate.getId())) {
			
				//if (gs.getMessage() != null) gs.setMessage(null);
				
				gstate = gs;
	
				log.info("[setGlobalState] Giro GlobalState completato! E' il mio turno (" + lstate.getId() + ")" );
	
				/* Se sono in gioco... */
				if (gstate.getCarte(lstate.getId()) != null) {
				
					/* Parte il thread per l'input */
					new WaitForInput(gstate).start();
				
				} else {
					gstate.setToken(gstate.getRing().getNext(lstate.getId()).getId());
					Message msg = new Message(Message.GS_UPDATE, gstate);
					threads.startSendThread(msg, gstate.getRing().getNext(lstate.getId()));
				}
					
			/* Se non è il mio turno */
			} else {
				log.info("[setGlobalState] Ho ricevuto una gstate diverso o non è il mio turno");	
				
				gstate = gs;		
				
				/* ..e inoltro */
				log.info("[setGlobalState] Inoltro il gstate a " + gstate.getRing().getNext(lstate.getId()).getNamePlayer());				//
				//Messaggio msg = new Messaggio(Messaggio.GS_UPDATE, gstate);
				threads.startSendThread(m, gstate.getRing().getNext(lstate.getId()));
				
				/* Aggiorno la grafica.. */
				gstate.notifyChanged(ui);
				
			}
			
			/* DEBUG */
			log.info("[setGlobalState] " + Debug.printPlayersList(gstate.getRing()));
			log.info("[setGlobalState] Leader = " + gstate.getLeader().getNamePlayer() + " - Mazziere = " + gstate.getMazziere().getNamePlayer());	
			
			break;
		
		// not used
		case Message.CRASH_ON_PING:
			
			gs = (GlobalState) m.getObject();
			
			Ring currentRing = gstate.getRing();
			Player currentMazziere = gstate.getMazziere();
			
			boolean mazCrash = !gs.getRing().contains(currentMazziere);
			
			if (!(m.getSender() != lstate.getId())) {
			
				Ring updatedRing = gs.getRing();
				
				for (Player p : currentRing) {
					if (!updatedRing.contains(p)) {
						gstate.clearPlayer(p);
					}
				}
				
				threads.startSendThread(m, gstate.getRing().getNext(lstate.getId()));
				
				gstate.notifyChanged(ui);
				
			} else {
				
				/* Se è crashato il mazziere... */
				if (mazCrash) {
					gstate.setMessage("Tavolo senza mazziere!\nRicomincio una nuova mano...");
					gstate.setMazziere(gstate.getRing().getMazziere());
					Util.carteCoperte(gstate);
					gstate.notifyChanged(ui);
				}
				
				gstate.setToken(gstate.getRing().getNext(gstate.getMazziere()).getId());
				Message msg = new Message(Message.FINE_MANO, gstate);
				threads.startSendThread(msg, gstate.getRing().getNext(lstate.getId()));
				
			}
			
			break;
			
		case Message.FINE_MANO:
			
			gstate = (GlobalState) m.getObject();
			
			//if (m.isValid()) {
			if (!(m.getSender() == lstate.getId())) {
				
				threads.startSendThread(m, gstate.getRing().getNext(lstate.getId()));
				log.info("invio fine mano a: " + gstate.getRing().getNext(lstate.getId()).getNamePlayer());

				gstate.notifyChanged(ui);
				
			} else {
				log.info("giro fine mano completato");
				
				gstate.setMessage(null);
				gstate.setToken(gstate.getRing().getNext(gstate.getMazziere()).getId());
				
				Message msg = new Message(Message.GS_UPDATE, gstate);
				threads.startSendThread(msg, gstate.getRing().getNext(lstate.getId()));
							
			}
			
			break;
			
		default:
			break;
		}
		
		if (!pingStarted) {
			pingStarted = true;
			threads.startPingThread(gstate);
		}
	}

	/* Ping */
	public int areYouAlive() throws RemoteException {
		return lstate.getId();
	}

}